package logger

import (
	"context"
	"sync"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

var (
	global *zap.Logger
	once   sync.Once
)

// Init initializes a zap logger with optional overrides.
func Init(service string, level string) (*zap.Logger, error) {
	var err error
	once.Do(func() {
		cfg := zap.NewProductionConfig()
		cfg.Level = parseLevel(level)
		cfg.InitialFields = map[string]interface{}{
			"service": service,
		}
		global, err = cfg.Build()
	})

	return global, err
}

func parseLevel(level string) zap.AtomicLevel {
	if level == "" {
		return zap.NewAtomicLevelAt(zapcore.InfoLevel)
	}
	var lvl zapcore.Level
	if err := lvl.UnmarshalText([]byte(level)); err != nil {
		return zap.NewAtomicLevelAt(zapcore.InfoLevel)
	}
	return zap.NewAtomicLevelAt(lvl)
}

// L returns the global logger. Init must be called beforehand.
func L() *zap.Logger {
	if global == nil {
		panic("logger: Init must be called before usage")
	}
	return global
}

// WithContext adds request scoped fields to a logger derived from context.
func WithContext(ctx context.Context) *zap.Logger {
	if ctxLogger, ok := ctx.Value(loggerKey{}).(*zap.Logger); ok {
		return ctxLogger
	}
	return L()
}

// Inject attaches logger to context for downstream usage.
func Inject(ctx context.Context, fields ...zap.Field) context.Context {
	return context.WithValue(ctx, loggerKey{}, L().With(fields...))
}

type loggerKey struct{}
